/* eslint-disable */
// import { Cartesian3, PrimitiveType, BlendingState } from 'cesium'
let { Cartesian3 } = Cesium
let { PrimitiveType } = Cesium
let { BlendingState } = Cesium

import CustomPrimitive from './CustomPrimitive'
import { getGeometry } from './getGeometry'

import Meshs from './Meshs'

const CLOUDCTT_SHADER = {
  // heitMin以下不绘制;heitMin以上尽可能的值越大时不透明度越大 sin函数
  VS: `
  attribute vec3 position;
  uniform mat4 projectionMatrix;
  uniform mat4 modelViewMatrix;
  attribute vec4 color;
  varying vec4 v_color;
  varying vec3 v_pt;
  void main(){
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      v_color = color;
      v_pt = position;
  }
  `,
  FS:
    `    
      #ifdef GL_ES
      precision highp float;
      #endif

      varying vec4 v_color;
      varying vec3 v_pt;   
      uniform float heitMin; 
      uniform float heitDelta; 
      float PI05 = 3.14159265/2.0;

      highp float getHeit(vec3 pt){  
          highp float scale = 0.0001;

          pt.x = pt.x * scale;
          pt.y = pt.y * scale;
          pt.z = pt.z * scale;

          highp float R = 6378137.0 * scale;
          highp float r = 6356752.3142451793 * scale;
          highp float R2 = R * R;
          highp float r2 = r * r; 

          highp float dR2 = pt.x * pt.x + pt.y * pt.y;
          highp float dis2 = dR2 + pt.z * pt.z;
          highp float heit = dis2 * R2 * r2 / (R2 * pt.z * pt.z + r2 * dR2);
          heit = sqrt(dis2) - sqrt(heit);
      
          return heit/scale;
      }

      void main(){
          highp float heit = getHeit(v_pt); 
          if(heit >= heitMin){
              float alpha = (heit - heitMin) / heitDelta;
              if(alpha < 0.0){ alpha = 0.0;}
              if(alpha > 1.0){ alpha = 1.0;}
              float alphaMin = 0.0;
              float alphaDelta = 1.0 - alphaMin;
              alpha = alpha * PI05;
              alpha = alphaMin + alphaDelta * sin(alpha);
              gl_FragColor = vec4(v_color.xyz,alpha);
          } 
      }
      `
}

const ERR_VALUE_CT = -999
const VALUE_MIN_CT = -90
const VALUE_MAX_CT = 60
const VALUE_MIN_SCALE_CT = 0.1 // 0~1 (vMax-v)/dv<VALUE_MIN_SCALE_CT不渲染 值越大高度越低
const HEIT_BOTTOM_CT = 1400 // 云底高
const HEIT_SPAN_CT = 300 // 云高跨度
const HEIT_SCALE_CT = 0.8 // 高度系数
const PERPRIMITI_NUMS_CT = 20000 // 每个primitive合并的三角面元最大数量

export function Point3(x, y, z) {
  this.x = x
  this.y = y
  this.z = z
}

export function Color4(r, g, b, a) {
  this.r = r
  this.g = g
  this.b = b
  this.a = a
}

export default class RainCloud {
  constructor(viewer) {
    this.viewer = viewer
    this.isLoadData = false
    this.isRending = false
    this.comDataInfo = {
      LonMin: 0,
      LonNums: 0,
      LonStep: 0,
      LonMax: 0,
      DLon: 0,
      LatMin: 0,
      LatNums: 0,
      LatStep: 0,
      LatMax: 0,
      DLat: 0,
      Mesh: new Meshs()
    }
    this.primitiveAry = []
    this.heitMin = 0
    this.heitDelta = 0
  }

  loadDataInfo(dataInfo) {
    // comInfo={Bound,DataAry}
    // Bound=[12]: 0-LayerMax 1-LonMin 2-LatMin 3-LayerNums 4-LonNums 5-LatNums
    // 6-DLayer 7-DLon 8-DLat 9-ValueScale 10-Stride 11-IsHpa 12-StrHpa
    // DataAry=[N] N= LatNums*LonNums
    this.removeAll()
    this.isLoadData = false
    this.comDataInfo.Mesh.clear()
    if (dataInfo.Bound.length < 10 || dataInfo.Bound[4] < 2 ||
      dataInfo.Bound[5] < 2 || dataInfo.Bound[7] < 0 || dataInfo.Bound[8] < 0 ||
      dataInfo.DataAry.length !== dataInfo.Bound[4] * dataInfo.Bound[5]) {
      return
    }
    let valueScale = dataInfo.Bound[9] ? dataInfo.Bound[9] : 1
    valueScale = 1.0 / dataInfo.Bound[9]

    const lonMin = dataInfo.Bound[1]
    const latMin = dataInfo.Bound[2]
    const lonNums = dataInfo.Bound[4]
    const latNums = dataInfo.Bound[5]
    const dLon = dataInfo.Bound[7]
    const dLat = dataInfo.Bound[8]
    const lonStep = dLon / (lonNums - 1)
    const latStep = dLat / (latNums - 1)

    this.comDataInfo.LonMin = lonMin
    this.comDataInfo.LatMin = latMin
    this.comDataInfo.LonNums = lonNums
    this.comDataInfo.LatNums = latNums
    this.comDataInfo.DLon = dLon
    this.comDataInfo.DLat = dLat
    this.comDataInfo.LonStep = lonStep
    this.comDataInfo.LatStep = latStep
    this.comDataInfo.LonMax = lonMin + dLon
    this.comDataInfo.LatMax = latMin + dLat

    const heitMax = HEIT_BOTTOM_CT + HEIT_SPAN_CT * HEIT_SCALE_CT
    this.heitMin = HEIT_BOTTOM_CT + HEIT_SPAN_CT * HEIT_SCALE_CT * VALUE_MIN_SCALE_CT
    this.heitDelta = heitMax - this.heitMin

    // 获取数据及数据的最小最大值
    let value, index
    let maxValue = VALUE_MIN_CT
    let minValue = VALUE_MAX_CT
    for (let i = 0; i < latNums; i++) {
      index = i * lonNums
      for (let j = 0; j < lonNums; j++) {
        value = dataInfo.DataAry[index + j]
        if (value !== ERR_VALUE_CT) {
          value = value * valueScale
          if (value < VALUE_MIN_CT) value = VALUE_MIN_CT
          if (value > VALUE_MAX_CT) value = VALUE_MAX_CT
          if (value < minValue) minValue = value
          if (value > maxValue) maxValue = value
        }
        dataInfo.DataAry[index + j] = value
      }
    }
    if (maxValue < minValue) {
      return
    }

    let isEq = false
    const dValue = maxValue - minValue
    if (dValue === 0) isEq = true

    // 获取每个格点的位置和颜色 初始化mesh顶点信息
    for (let i = 0; i < latNums; i++) {
      index = i * lonNums
      const lat = latMin + i * latStep
      for (let j = 0; j < lonNums; j++) {
        value = dataInfo.DataAry[index + j]
        const lon = lonMin + j * lonStep
        let heit = HEIT_BOTTOM_CT
        if (value !== ERR_VALUE_CT) {
          if (!isEq) {
            let dt = (maxValue - value) / dValue
            if (dt < 0) dt = 0
            if (dt > 1) dt = 1
            heit = HEIT_BOTTOM_CT + HEIT_SPAN_CT * HEIT_SCALE_CT * dt
          }
        }
        const pt = Cartesian3.fromDegrees(lon, lat, heit)
        this.comDataInfo.Mesh.addVertex(new Point3(pt.x, pt.y, pt.z),
          new Color4(0.8, 0.8, 0.8, 1))
      }
    }

    // 初始化mesh三角面元信息
    for (let i = 0; i < latNums - 1; i++) {
      const indexR0 = i * lonNums
      const indexR1 = (i + 1) * lonNums
      for (let j = 0; j < lonNums - 1; j++) {
        const value00 = dataInfo.DataAry[indexR0 + j]
        const value01 = dataInfo.DataAry[indexR0 + j + 1]
        const value10 = dataInfo.DataAry[indexR1 + j]
        const value11 = dataInfo.DataAry[indexR1 + j + 1]
        if (value00 === ERR_VALUE_CT || value01 === ERR_VALUE_CT || value10 === ERR_VALUE_CT || value11 === ERR_VALUE_CT) {
          continue
        }
        const index00 = indexR0 + j
        const index01 = indexR0 + j + 1
        const index10 = indexR1 + j
        const index11 = indexR1 + j + 1
        this.comDataInfo.Mesh.addFace(index00, index01, index10)
        this.comDataInfo.Mesh.addFace(index01, index11, index10)
      }
    }

    // 平滑处理
    this.comDataInfo.Mesh.laplacianSmooth(10)
    // this.comDataInfo.Mesh.taubinSmooth(2, 0.5, -0.5)
    // this.comDataInfo.Mesh.hcLaplacianSmooth(2, 1, 0.5)
    this.isLoadData = true

  }

  removeAll() {
    for (let i = 0; i < this.primitiveAry.length; i++) {
      this.viewer.scene.primitives.remove(this.primitiveAry[i])
      this.primitiveAry[i].destroy()
      this.primitiveAry[i] = undefined
    }
    this.primitiveAry = []
  }

  changeShow(isShow) {
    for (let i = 0; i < this.primitiveAry.length; i++) {
      this.primitiveAry[i].show = isShow
    }
  }

  renderCloudCTT() {
    if (!this.isLoadData || this.isRending || this.primitiveAry.length > 0) {
      return
    }

    this.isRending = true
    let index = 0
    let positions = []
    let colors = []
    let indices = []
    const mesh = this.comDataInfo.Mesh
    const shaderFS = CLOUDCTT_SHADER.FS
    const shaderVS = CLOUDCTT_SHADER.VS
    for (let i = 0; i < mesh.faceAry.length; i++) {
      if (i !== 0 && i % PERPRIMITI_NUMS_CT === 0) {
        this._renderGeo(positions, colors, new Uint16Array(indices), shaderFS, shaderVS)
        positions = []
        colors = []
        indices = []
        index = 0
      }
      const face = mesh.faceAry[i].face
      const p0index = face.p0Index
      const p1index = face.p1Index
      const p2index = face.p2Index

      const p0 = mesh.verticeAry[p0index]
      const p1 = mesh.verticeAry[p1index]
      const p2 = mesh.verticeAry[p2index]
      const c0 = mesh.perVerColorAry[p0index]
      const c1 = mesh.perVerColorAry[p1index]
      const c2 = mesh.perVerColorAry[p2index]

      positions.push(
        p0.x, p0.y, p0.z,
        p1.x, p1.y, p1.z,
        p2.x, p2.y, p2.z)
      colors.push(
        c0.r, c0.g, c0.b, c0.a,
        c1.r, c1.g, c1.b, c1.a,
        c2.r, c2.g, c2.b, c2.a)
      const indexAdd = 3 * index
      indices.push(indexAdd + 0, indexAdd + 1, indexAdd + 2)
      index++
    }

    if (positions.length > 0) {
      this._renderGeo(positions, colors, new Uint16Array(indices), shaderFS, shaderVS)
    }

    this.isRending = false
  }

  _renderGeo(positionAry, colorAry, indiceAry, shaderFS, shaderVS) {
    const uniformMap = new Map()
    const heitMin = this.heitMin
    const heitDelta = this.heitDelta
    const heitMinFn = function () {
      return heitMin
    }
    const heitDeltaFn = function () {
      return heitDelta
    }
    uniformMap.set('heitMin', heitMinFn)
    uniformMap.set('heitDelta', heitDeltaFn)

    const customPrimitiveOption = {
      geometry: getGeometry(positionAry, colorAry, indiceAry),
      attributeLocations: {
        position: 0,
        color: 1
      },
      primitiveType: PrimitiveType.TRIANGLES,
      renderState: {
        depthTest: {
          enabled: true
        },
        depthMask: true,
        blending: BlendingState.ALPHA_BLEND
      },
      vShader: shaderVS,
      fShader: shaderFS,
      uniformMap: uniformMap
    }

    const customPrimitive = new CustomPrimitive(customPrimitiveOption)
    this.viewer.scene.primitives.add(customPrimitive)
    this.primitiveAry.push(customPrimitive)
  }
}
